﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "RevitElement.h"
#include "AuxiliaryElementUtils.h"
#include "DbObjectUtils.h"
#include "GbmpBuiltInCategoryUniIdentitiesNew.h"
#include "IBody.h"
#include "IDocument.h"
#include "IElement.h"
#include "IElementBasicInformation.h"
#include "IElementModelShape.h"
#include "IElementParameterBindings.h"
#include "IElementParameters.h"
#include "IElementParentReporter.h"
#include "IElementPosition.h"
#include "IElementShapeHandle.h"
#include "IGenericElement.h"
#include "IGraphicsBRepBody.h"
#include "IGraphicsElementShape.h"
#include "IGraphicsStyleManager.h"
#include "IParameter.h"
#include "IParameterBinding.h"
#include "IParameterValueElementId.h"
#include "RevitElementBehaviors.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;
using namespace std;

#pragma region 基本属性

DBOBJECT_DATA_DEFINE(RevitElement)
{
    SetOwnerElement(nullptr);
}

RevitElement* RevitElement::Create(gcmp::IDocument* pDoc)
{
    OwnerPtr<RevitElement> opRevitElement = NEW_AS_OWNER_PTR(RevitElement);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opRevitElement, L"创建opRevitElement失败",L"GDMPLab",L"2024-03-30");

    RevitElement* pRevitElement = opRevitElement.get();
    IGenericElement* pGenericElement = IGenericElement::Create(
        pDoc, 
        TransferOwnership(opRevitElement),
        RevitElement::GetClassId().GetGuid(), 
        ElementCreationOptions::Normal
    );

    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGenericElement, L"创建IGenericElement失败",L"GDMPLab",L"2024-03-30");
    pGenericElement->SetElementTransformationComponent(NEW_AS_OWNER_PTR(RevitElementTransformationComponent));
    IElementBasicInformation* pBasicInformation = pGenericElement->GetBasicInformation();
    pBasicInformation->SetCategoryUid(BuiltInCategoryUniIdentities::BICU_REVIT_ELEMENT);
    
    return pRevitElement;
}

bool RevitElement::UpdateShape(const vector<wstring>& ggpStrArray)
{
    IElement* pElement = GetOwnerElement();
    IElementBasicInformation* pBasicInformation = pElement->GetBasicInformation();
    const UniIdentity& categoryUid = pBasicInformation->GetCategoryUid();
    ElementId styleId = GetDocument()->GetGraphicsStyleManager()->GetGraphicsStyleIdByCategoryUid(categoryUid);

    gcmp::OwnerPtr<IGraphicsElementShape> opGRep = IGraphicsElementShape::Create(GraphicsRenderLayer::Model);
    opGRep->SetElementId(pElement->GetElementId());
    opGRep->SetGraphicsStyleId(styleId);

    int bodyNodeId = 0;
    FOR_EACH(str, ggpStrArray)
    {
        OwnerPtr<IGeometry> opBody = IGeometry::CreateFromString(str);
        OwnerPtr<IGraphicsBRepBody> opBRepBody = IGraphicsBRepBody::Create(TransferOwnershipCast<IBody>(opBody));
        opBRepBody->SetId(GraphicsNodeId(bodyNodeId++));
        opGRep->AddChild(TransferOwnership(opBRepBody));
    }

    IElementModelShape* pGraphicsElementShapeBehavior = pElement->GetElementModelShape();
    pGraphicsElementShapeBehavior->SetGraphicsElementShape(TransferOwnership(opGRep));

    return true;
}

const RevitElement* RevitElement::Get(const IDocument* pDoc, const ElementId& id)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    const IGenericElement *pGenericElement = quick_cast<IGenericElement>(pDoc->GetElement(id));
    if (pGenericElement && pGenericElement->GetExternalObject())
    {
        const RevitElement *pRvtElem = quick_cast<RevitElement>(pGenericElement->GetExternalObject());
        return pRvtElem;
    }

    return nullptr;
}

RevitElement * RevitElement::GetFw(IDocument * pDoc, const ElementId& id)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    IGenericElement *pGenericElement = quick_cast<IGenericElement>(pDoc->GetElement(id));
    if (pGenericElement && pGenericElement->GetExternalObject())
    {
        RevitElement *pRvtElem = quick_cast<RevitElement>(pGenericElement->GetExternalObject());
        return pRvtElem;
    }

    return nullptr;
}

bool RevitElement::UpdateShape(OwnerPtr<IBody> opBody)
{
    IElement* pElement = GetOwnerElement();
    IElementBasicInformation* pBasicInformation = pElement->GetBasicInformation();
    const UniIdentity& categoryUid = pBasicInformation->GetCategoryUid();
    ElementId styleId = GetDocument()->GetGraphicsStyleManager()->GetGraphicsStyleIdByCategoryUid(categoryUid);

    gcmp::OwnerPtr<IGraphicsElementShape> opGRep = IGraphicsElementShape::Create(GraphicsRenderLayer::Model);
    opGRep->SetElementId(pElement->GetElementId());
    opGRep->SetGraphicsStyleId(styleId);
    opGRep->AddChild(IGraphicsBRepBody::Create(TransferOwnership(opBody)));

    IElementModelShape* pGraphicsElementShapeBehavior = pElement->GetElementModelShape();
    pGraphicsElementShapeBehavior->SetGraphicsElementShape(TransferOwnership(opGRep));

    return true;
}

ElementId gcmp::RevitElement::GetOwnerElementId() const
{
    if (!m_pOwnerElement)
    {
        return ElementId::InvalidID;
    }
    return m_pOwnerElement->GetElementId();
}

IDocument* gcmp::RevitElement::GetDocument() const
{
    if (!m_pOwnerElement)
    {
        return nullptr;
    }
    return m_pOwnerElement->GetDocument();
}

gcmp::NdbObject* gcmp::RevitElement::GetTopOwnerObject() const
{
    return quick_cast<NdbObject>(const_cast<IElement*>(GetOwnerElement()));
}

#pragma endregion 基本属性

#pragma region 关联更新

void gcmp::RevitElement::UpdateForWeakParentDeletion(const std::set<ElementId>& deletedElementIds)
{
    //参数绑定，并且绑定的不是族参数时
    IElement* pElement = GetOwnerElement();
    IElementParameterBindings* pBehavior = pElement->GetElementParameters()->GetElementParameterBindings();
    if (pBehavior)
    {
        auto& bindingInfos = pBehavior->GetParameterBindings();
        for (int ii = (int)bindingInfos.size(); ii > 0; --ii)
        {
            const IParameterBinding* info = bindingInfos[ii - 1].get();
            IElement* pSourceElement = GetDocument()->GetElement(info->GetSourceElementId());
            if (pSourceElement == nullptr)
            {
                continue;
            }

            IElementParameterBindings* pBingBehavior = pSourceElement->GetElementParameters()->GetElementParameterBindings();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pBingBehavior, L"pBingBehavior无效",L"GDMPLab",L"2024-03-30");

            std::vector<RegenDataId> rcDataIds = pBingBehavior->GetParameterRdIdsByParameterDefinitionId(info->GetSourceParameterDefinitionId());
            std::size_t size = rcDataIds.size();
            if (size > 0 && size != 2) // IFamilyManager为2
            {
                if (deletedElementIds.find(info->GetSourceElementId()) != deletedElementIds.end())
                {
                    pBehavior->DeleteParameterBinding(info->GetTargetParameterDefinitionId());
                }
            }
        }
    }
}

void gcmp::RevitElement::ReportParents(IElementParentReporter& reporter) const
{
    const IElement* pElement = GetOwnerElement();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"IElement为空",L"GDMPLab",L"2024-03-30");
    const IElementPosition *posBehavior = pElement->GetElementPosition();
    if (posBehavior != nullptr)
    {
        reporter.ReportStrong(posBehavior->GetBaseAssociatedPlaneId());
    }

    const IElementParameterBindings* pBehavior = GetOwnerElement()->GetElementParameters()->GetElementParameterBindings();
    if (pBehavior)
    {
        auto& bindingInfos = pBehavior->GetParameterBindings();
        FOR_EACH(info, bindingInfos)
        {
            IElement* pSourceElement = GetDocument()->GetElement(info->GetSourceElementId());
            if (pSourceElement != nullptr)
            {
                reporter.ReportWeak(pSourceElement->GetElementId());
            }
        }
    }

    auto& embeddedParamMap = GetOwnerElement()->GetElementParameters()->GetEmbeddedParametersMap();
    FOR_EACH(iter, embeddedParamMap)
    {
        const IParameterValueElementId *pElementIdParamValue = dynamic_cast<const IParameterValueElementId *>(iter.second->GetParameterValueStorage());
        if (pElementIdParamValue != nullptr)
        {
            reporter.ReportWeak(pElementIdParamValue->GetValue());
        }
    }

    reporter.ReportStrong(GetOwnerElement()->GetBasicInformation()->GetTypeId());

    const IGraphicsElementShape* pGrep = pElement->GetElementModelShape()->GetGraphicsElementShape();
    if (pGrep)
    {
        std::vector<ElementId> ids;
        pGrep->GetReferencedElementIds(ids);
        reporter.ReportWeak(ids);
    }
}

#pragma endregion 关联更新

#pragma region 其他
bool gcmp::RevitElement::FinishEditing()
{
    std::vector<ElementId> ids = AuxiliaryElementUtils::GetAuxiliaryElements(GetOwnerElement());
    for (int i = 0; i < (int)ids.size(); i++)
    {
        IElement* pElement = GetDocument()->GetElement(ids[i]);
        IElementShapeHandle* pElementShapeHandle = quick_cast<IElementShapeHandle>(pElement);
        if (pElementShapeHandle)
        {
            pElementShapeHandle->FinishEditing();
            continue;
        }
    }
    return true;
}

#pragma endregion 其他

#pragma region protected

bool gcmp::RevitElement::SetOwnerElement(IElement* pOwnerElement)
{
    m_pOwnerElement = pOwnerElement;
    return true;
}

#pragma endregion protected